import("//clang/runtimes.gni")
import("//libcxx/config.gni")
import("//llvm/utils/gn/build/symlink_or_copy.gni")

declare_args() {
  # Whether to support libc++ opt-in debug mode via _LIBCPP_DEBUG.
  libcxx_enable_debug_mode = true

  # Build libc++ with definitions for operator new/delete.
  libcxx_enable_new_delete_definitions = true

  # Build libc++ as a shared library.
  libcxx_enable_shared = true

  # Build libc++ as a static library.
  libcxx_enable_static = true

  # Build filesystem as part of libc++fs.a.
  libcxx_enable_filesystem = target_os != "win"

  # Build libc++experimental.a.
  libcxx_enable_experimental = true

  # Use compiler-rt builtins.
  libcxx_use_compiler_rt = true

  # Use exceptions.
  libcxx_enable_exceptions = true

  # Use run time type information.
  libcxx_enable_rtti = true

  # Do not export any symbols from the static library.
  libcxx_hermetic_static_library = true

  # Use and install a linker script for the given ABI library.
  libcxx_enable_abi_linker_script = true
}

config("cxx_config") {
  include_dirs = [ "//libcxxabi/include" ]
  cflags = [
    "-Wall",
    "-Wextra",
    "-W",
    "-Wwrite-strings",
    "-Wno-unused-parameter",
    "-Wno-long-long",
    "-Werror=return-type",
    "-Wextra-semi",
    "-Wno-user-defined-literals",
    "-Wno-covered-switch-default",
  ]
  cflags_cc = [
    "-std=c++20",
    "-nostdinc++",
  ]
  defines = [ "_LIBCPP_BUILDING_LIBRARY" ]
  if (target_os == "win") {
    cflags += [ "/Zl" ]
    defines += [
      # Ignore the -MSC_VER mismatch, as we may build
      # with a different compatibility version.
      "_ALLOW_MSC_VER_MISMATCH",

      # Don't check the msvcprt iterator debug levels
      # as we will define the iterator types; libc++
      # uses a different macro to identify the debug
      # level.
      "_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH",

      # We are building the c++ runtime, don't pull in
      # msvcprt.
      "_CRTBLD",

      # Don't warn on the use of "deprecated"
      # "insecure" functions which are standards
      # specified.
      "_CRT_SECURE_NO_WARNINGS",

      # Use the ISO conforming behaviour for conversion
      # in printf, scanf.
      "_CRT_STDIO_ISO_WIDE_SPECIFIERS",
    ]
  }
  if (!libcxx_enable_new_delete_definitions) {
    defines += [ "_LIBCPP_DISABLE_NEW_DELETE_DEFINITIONS" ]
  }
  if (libcxx_enable_exceptions) {
    if (current_os == "win") {
      cflags_cc += [ "/EHsc" ]
    }
  } else {
    if (current_os == "win") {
      cflags_cc += [
        "/EHs-",
        "/EHa-",
      ]
    } else {
      cflags_cc += [ "-fno-exceptions" ]
    }
  }
  if (!libcxx_enable_rtti) {
    if (current_os == "win") {
      cflags_cc += [ "/GR-" ]
    } else {
      cflags_cc += [ "-fno-rtti" ]
    }
  }
}

cxx_sources = [
  "algorithm.cpp",
  "any.cpp",
  "atomic.cpp",
  "barrier.cpp",
  "bind.cpp",
  "charconv.cpp",
  "chrono.cpp",
  "condition_variable.cpp",
  "condition_variable_destructor.cpp",
  "exception.cpp",
  "format.cpp",
  "functional.cpp",
  "future.cpp",
  "hash.cpp",
  "include/apple_availability.h",
  "include/atomic_support.h",
  "include/config_elast.h",
  "include/refstring.h",
  "include/sso_allocator.h",
  "ios.cpp",
  "ios.instantiations.cpp",
  "iostream.cpp",
  "locale.cpp",
  "memory.cpp",
  "mutex.cpp",
  "mutex_destructor.cpp",
  "new.cpp",
  "optional.cpp",
  "random.cpp",
  "random_shuffle.cpp",
  "regex.cpp",
  "shared_mutex.cpp",
  "stdexcept.cpp",
  "string.cpp",
  "strstream.cpp",
  "support/runtime/exception_fallback.ipp",
  "support/runtime/exception_glibcxx.ipp",
  "support/runtime/exception_libcxxabi.ipp",
  "support/runtime/exception_libcxxrt.ipp",
  "support/runtime/exception_msvc.ipp",
  "support/runtime/exception_pointer_cxxabi.ipp",
  "support/runtime/exception_pointer_glibcxx.ipp",
  "support/runtime/exception_pointer_msvc.ipp",
  "support/runtime/exception_pointer_unimplemented.ipp",
  "support/runtime/new_handler_fallback.ipp",
  "support/runtime/stdexcept_default.ipp",
  "support/runtime/stdexcept_vcruntime.ipp",
  "system_error.cpp",
  "thread.cpp",
  "typeinfo.cpp",
  "utility.cpp",
  "valarray.cpp",
  "variant.cpp",
  "vector.cpp",
]
if (target_os == "win") {
  cxx_sources += [
    "support/win32/locale_win32.cpp",
    "support/win32/support.cpp",
    "support/win32/thread_win32.cpp",
  ]
}
if (target_os == "solaris") {
  cxx_sources += [ "support/solaris/xlocale.cpp" ]
}
if (target_os == "zos") {
  cxx_sources += [ "support/ibm/xlocale_zos.cpp" ]
}
if (libcxx_enable_debug_mode) {
  cxx_sources += [ "debug.cpp" ]
}
if (libcxx_enable_filesystem) {
  cxx_sources += [
    "filesystem/directory_iterator.cpp",
    "filesystem/filesystem_common.h",
    "filesystem/operations.cpp",
    "filesystem/posix_compat.h",
  ]
  if (libcxx_use_compiler_rt) {
    cxx_sources += [ "filesystem/int128_builtins.cpp" ]
  }
}

if (libcxx_enable_shared) {
  shared_library("cxx_shared") {
    output_dir = runtimes_dir
    output_name = "c++"
    if (libcxx_enable_abi_linker_script) {
      output_extension = "so.0"
    }
    if (target_os == "linux" || target_os == "mac") {
      cflags = [ "-fPIC" ]
      ldflags = [ "-nostdlib++" ]
      libs = [
        "dl",
        "pthread",
      ]
    }
    sources = cxx_sources
    deps = [
      "//compiler-rt/lib/builtins",
      "//libcxx/include",
      "//libcxxabi/src:cxxabi_shared",
      "//libunwind/src:unwind_shared",
    ]
    configs += [ ":cxx_config" ]
    configs -= [
      "//llvm/utils/gn/build:no_exceptions",
      "//llvm/utils/gn/build:no_rtti",
    ]
  }

  symlink_or_copy("cxx_symlink") {
    deps = [ ":cxx_shared" ]
    source = "libc++.so.0"
    output = "$runtimes_dir/libc++.so"
  }

  if (libcxx_enable_abi_linker_script) {
    action("cxx_linker_script") {
      script = "//llvm/utils/gn/secondary/libcxx/utils/gen_link_script.py"
      outputs = [ "$runtimes_dir/libc++.so" ]
      args = [
        "--input",
        rebase_path("$runtimes_dir/libc++.so.0", root_build_dir),
        "--output",
        rebase_path("$runtimes_dir/libc++.so", root_build_dir),
        "c++abi",
        "unwind",
      ]
      deps = [ ":cxx_symlink" ]
    }
  }
}

if (libcxx_enable_static) {
  static_library("cxx_static") {
    output_dir = runtimes_dir
    output_name = "c++"
    complete_static_lib = true
    configs -= [ "//llvm/utils/gn/build:thin_archive" ]
    sources = cxx_sources
    if (libcxx_hermetic_static_library) {
      cflags = [ "-fvisibility=hidden" ]
      if (libcxx_enable_new_delete_definitions) {
        cflags_cc = [ "-fvisibility-global-new-delete-hidden" ]
      }
      defines = [ "_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS" ]
    }
    deps = [
      "//compiler-rt/lib/builtins",
      "//libcxx/include",
      "//libcxxabi/src:cxxabi_static",
      "//libunwind/src:unwind_static",
    ]
    configs += [ ":cxx_config" ]
    configs -= [
      "//llvm/utils/gn/build:no_exceptions",
      "//llvm/utils/gn/build:no_rtti",
    ]
  }
}

if (libcxx_enable_experimental) {
  static_library("cxx_experimental") {
    output_dir = runtimes_dir
    output_name = "c++experimental"
    sources = [ "experimental/memory_resource.cpp" ]
    deps = [ "//libcxx/include" ]
    configs += [ ":cxx_config" ]
    configs -= [
      "//llvm/utils/gn/build:no_exceptions",
      "//llvm/utils/gn/build:no_rtti",
    ]
  }
}

group("src") {
  deps = []
  if (libcxx_enable_shared) {
    if (libcxx_enable_abi_linker_script) {
      deps += [ ":cxx_linker_script" ]
    } else {
      deps += [ ":cxx_shared" ]
    }
  }
  if (libcxx_enable_static) {
    deps += [ ":cxx_static" ]
  }
  if (libcxx_enable_experimental) {
    deps += [ ":cxx_experimental" ]
  }
}
